package com.run2code.starter.lock.redisson;

import com.run2code.starter.lock.DistributedLock;
import com.run2code.starter.lock.DistributedLockException;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * Redisson分布式锁实现
 * 关注公众号【奔跑的码畜】，一起进步不迷路
 *
 * @author 第七人格
 * @date 2024/06/17
 */
// 使用@Component注解，表明这是一个Spring组件，Spring会自动检测并实例化这个类
@Component
// 使用@ConditionalOnClass注解，表明这个组件的创建依赖于Redisson.class和RedisOperations.class是否存在于类路径中
@ConditionalOnClass({Redisson.class, RedisOperations.class})
public class RedissonDistributedLock implements DistributedLock {
    // 创建一个Logger对象，用于记录日志，这个对象是针对RedissonDistributedLock类的
    private static final Logger log = LoggerFactory.getLogger(RedissonDistributedLock.class);
    // 定义一个RedissonClient类型的成员变量，这个变量将用于与Redis进行交互
    private final RedissonClient redissonClient;

    // 定义一个lock方法，接收一个String类型的key作为参数，尝试获取对应的锁，并立即返回获取结果
    public boolean lock(String key) {
        // 使用redissonClient获取对应的锁，并尝试加锁，如果加锁成功则返回true，否则返回false
        return this.redissonClient.getLock(key).tryLock();
    }

    /**
     * 接收key、等待时间和租约时间作为参数，尝试在指定的等待时间内获取锁，并在获取锁后设置指定的租约时间
     *
     * @param key 加锁key
     * @param waitTime 等待时间
     * @param leaseTime 过期时间
     * @return boolean
     */
    public boolean lock(String key, int waitTime, int leaseTime) {
        try {
            // 使用redissonClient获取对应的锁，尝试在指定的等待时间内加锁，并设置租约时间
            // 如果在等待时间内成功获取锁，则返回true，否则返回false
            return this.redissonClient.getLock(key).tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // 如果在等待过程中线程被中断，则记录错误信息，并返回false表示获取锁失败
            log.error("获取锁失败 - {} - {}", new Object[]{key, waitTime, e});
            return false;
        }
    }

    /**
     * 接收一个String类型的key作为参数，用于释放对应的锁
     *
     * @param key 加锁key
     */
    public void unlock(String key) {
        try {
            // 使用redissonClient获取对应的锁，并释放它
            this.redissonClient.getLock(key).unlock();
        } catch (Exception e) {
            // 如果在释放锁的过程中出现异常，则抛出一个DistributedLockException异常，将原始的异常作为其原因
            throw new DistributedLockException(key, e);
        }
    }

    // 构造函数，接收一个RedissonClient对象作为参数，并将其赋值给成员变量redissonClient
    public RedissonDistributedLock(final RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }
}
